var extend = function(target, ...objs){
    if (target instanceof Object){
        for (let oid = 0; oid < objs.length; oid++){
            let obj = objs[oid];
            if (obj != null){
                for (let x in obj){
                    if (x !== undefined)
                        target[x] = obj[x];
                }
            }
        }
    }
};

var extendByClass = function(target, classFunc, ...args){
    if (target instanceof Object && classFunc instanceof Function){
        target['__proto__'] = classFunc.prototype;
        classFunc.call(target, ...args);
    }
};

var extendClass = function(target, ...objs){
    if (target instanceof Function){
        for (let oid = 0; oid < objs.length; oid++){
            let obj = objs[oid];
            if (obj != null){
                for (let x in obj){
                    if (x !== undefined)
                        target.prototype[x] = obj[x];
                }
            }
        }
    }
};

var clone = function (objfrom, option, pattern, _recursion) {
    let type = typeof objfrom;
    switch (type){
        case 'undefined':
        case 'boolean':
        case 'number':
        case 'symbol':
            return objfrom;
        case 'string':
            return objfrom instanceof String ? new String(objfrom.valueOf()) : objfrom;
    }
    // Null
    if (objfrom === null)
        return objfrom;

    // Circulation || Reference
    pattern = pattern || 0;
    if (_recursion == undefined){
        clone.refsFrom = [];
        clone.refsTo = [];
    }
    for (let xdx in clone.refsFrom){
        if (clone.refsFrom[xdx] == objfrom) {
            return clone.refsTo[xdx];
        }
    }

    let objto;

    // Generic Object
    if (objfrom instanceof Date){
        objto = new Date(objfrom.valueOf());
    } else if (objfrom instanceof RegExp){
        objto = new RegExp(objfrom.valueOf());
    } else if (objfrom instanceof Function){
        let fcstr = objfrom.toString();
        if (fcstr.indexOf('native code') > -1){
            objto = objfrom;
        }else{
            objto = eval('(' + fcstr + ')');
        }
    } else if (objfrom instanceof Array) {
        objto = new Array(); // give members by following for...in clause
    } else{
        try {
            objto = new objfrom.constructor();
        } catch (err){
        }
    }
    if (objto == null)
        objto = new Object();

    // Give Members, Iterating
    let arrbottom = clone.refsFrom.length;
    clone.refsFrom[arrbottom] = objfrom;
    clone.refsTo[arrbottom] = objto;
    if (pattern instanceof Object){
        for (let idx in pattern){
            if (idx !== undefined && objfrom[idx] !== undefined){
                let val = clone(objfrom[idx], pattern[idx], true);
                if (val != undefined)
                    objto[idx] = val;
            }
        }
    }else{
        for (let idx in objfrom){
            if (idx !== undefined) {
                let val = clone(objfrom[idx], pattern[idx], true);
                if (val != undefined)
                    objto[idx] = val;
            }
        }
    }

    if (objto !== {})
        return objto;

    // last effort, use JSON
    try{
        return JSON.parse(JSON.stringify(objfrom));
    }catch(err){
        return undefined;
    }
};

var keyArray = function(obj)
    let list = [];
    for (let x in obj){
        if (x !== undefined)
            list.push(x);
    }
    return list;
};

module.exports = {
    extend,
    extendByClass,
    extendClass,
    clone,
    keyArray
};